package net.threeple.springjdbc.helper;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.Map;
import java.util.Set;

import javax.persistence.Transient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.jdbc.core.RowMapper;

import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;

import net.threeple.springjdbc.helper.exception.FieldDoesNotExistException;

public abstract class EntityMapper<T> {
	static final Logger logger = LoggerFactory.getLogger(EntityMapper.class);
	private Class<?> entityClass;
	private Map<String, String> fieldsMapper = Maps.newHashMap();
	private String[] columns;

	public EntityMapper() {
		TypeToken<T> typeToken = new TypeToken<T>(getClass()) {
			private static final long serialVersionUID = -5164785786896354071L;
		};
		entityClass = typeToken.getRawType();
		addFieldNames(entityClass);
		Class<?> superClass = entityClass.getSuperclass();
		while (superClass != null) {
			addFieldNames(superClass);
			superClass = superClass.getSuperclass();
		}
	}
	
	private void addFieldNames(Class<?> entityClass) {
		Field[] fields = entityClass.getDeclaredFields();
		for (Field field : fields) {
			if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())
					|| field.isAnnotationPresent(Transient.class)) {
				continue;
			}
			fieldsMapper.put(field.getName(), field.getType().getSimpleName());
		}
	}

	public RowMapper<T> generateRowMapper() {
		final String[] columns = this.columns;
		return new RowMapper<T>() {

			@Override
			public T mapRow(ResultSet rs, int rowNum) throws SQLException {
				return generate(rs, columns);
			}

		};
	}

	@SuppressWarnings("unchecked")
	private T generate(ResultSet rs, String[] columns) {
		BeanWrapper bean = null;
		try {
			bean = new BeanWrapperImpl(entityClass.newInstance());
			String dataType = Date.class.getSimpleName();
			Set<String> fieldNames = fieldsMapper.keySet();
			for (String column : columns) {
				if(!fieldNames.contains(column)) {
					throw new FieldDoesNotExistException("类" + entityClass.getSimpleName() + "中不存在域" + column);
				}
				Object value = rs.getObject(column);
				if (value != null) {
					if (!fieldsMapper.get(column).equals(dataType)) {
						bean.setPropertyValue(column, value);
					} else {
						bean.setPropertyValue(column, rs.getTimestamp(column));
					}
				}
			}
			this.columns = null;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (BeansException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		return (T) bean.getWrappedInstance();
	}
	
	public EntityMapper<T> all() {
		this.columns = fieldsMapper.keySet().toArray(new String[0]);
		return this;
	}

	public EntityMapper<T> of(String[] columns) {
		this.columns = columns;
		return this;
	}

	public EntityMapper<T> except(String[] columns) {
		Set<String> fields = fieldsMapper.keySet();
		for(String column : columns) {
			fields.remove(column);
		}
		this.columns = fields.toArray(new String[0]);
		return this;
	}
	
	
}
